浅谈Vite

1. Vite 简介

1.1 Vite是什么?

Vite是一个由原生 ES Module 驱动的 Web 开发前端构建工具,它能显著改善前端开发体验;
要由两部分组成:

阅读全文

前端图片压缩

使用VUE或者React框架加载HTML大图时极容易导致前端页面卡死报out of memory错误,报出这个错误还好,如果没有报错浏览器可能就无法操作了。

阅读全文

JavaScript中Worker初探

什么是Worker?他有什么作用?

因为js是单线程运行的,在遇到一些需要处理大量耗时的数据时,可能会阻塞页面的加载,造成面的假死。针对这个问题,H5出了新的功能worker,支持多线程,这时我们可以使用worker来开辟一个独立于主线程的子线程来进行一些耗时的大量运算。就可以用来解决因为大量耗时运算是造成页面假死的问题,详细内容查看Worker

如何使用Worker

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//首先创建 Worker 对象,第一个参数 worker 需要执行的 JS 文件的 URL。相对地址 URL 是相对于创建 Worker 的脚本所在文档(宿主文档)的访问地址。绝对地址 URL 必须要和宿主文档同源。
//第二个参数是可选参数,可以是一个对象,name属性,可以给当前Worker定义一个名称,可用来区分其他的worker
const worker = new Worker("worker.js",{name:'自定义的名称'})

//通过onmessage ,监听子线程的消息
worker.onmessage((e)=>`接收worker线程传递值:${e.data}`)
//通过postMessage ,向子线程发送消息
worker.postMessage('向子线程发送消息')

//worker.js 文件

//通过self.onmessage,来接收主线程传递的信息
self.onmessage((e)=>{
console.log(e)
})
//通过self.postMessage,来向主线程传递信息
self.postMessage('要传递的值')

方法和属性

主线程worker()对象,用来供主线程操作 Worker。worker 线程对象的属性和方法如下:

  • worker.onerror:指定 error 事件的监听函数。
  • worker.onmessage:指定 message 事件的监听函数,发送过来的数据在Event.data属性中。
  • worker.onmessageerror:指定 messageerror 事件的监听函数。发送的数据无法序列化成字符串时,会触发这个事件。
  • worker.postMessage():向 Worker 线程发送消息。
  • worker.terminate():立即终止 Worker 线程。

worker 子线程全局属性和方法:

  • self.onmessage:指定message事件的监听函数。
  • self.onmessageerror:指定 messageerror 事件的监听函数。发送的数据无法序列化成字符串时,会触发这个事件。
  • self.close():关闭 worker 线程。
  • self.postMessage():向产生这个 worker 线程发送消息。
  • self.importScripts():加载 JS 脚本。
  • self.name: worker 的名字。该属性只读,由构造函数指定。

worker 使用注意点

  1. 同源限制

分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。

  1. DOM 限制

worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用document、window、parent这些对象。但是,Worker 线程可以navigator对象和location对象。

  1. 通信联系

worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息传递完成。

  1. 脚本限制

worker 线程不能执行alert()方法和confirm()方法,但可以使用 XMLHttpRequest 对象发出 AJAX 请求。

  1. 文件限制

worker 线程无法读取本地文件,即不能打开本机的文件系统(file://),它所加载的脚本,必须来自网络。

简单使用demo

模拟页面中插入耗时处理数据然后渲染元素,暂时不考虑页面渲染性能问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
(() => {
let arrList = []  
let indexNum = 1000
for (let index = 0; index < indexNum; index++) {
let obj = {
name: index + 1
}
arrList.push(obj)
if (arrList.length == indexNum) {
//在子线程中,self代表worker子线程的全局对象,也可以用this代替self,或者省略也行
//this.postMessage(arrList);
//self.postMessage(arrList);
postMessage(arrList); //向主线程发送消息
}
onmessage = function(e) {
console.log(e.data)
console.log("我是worker线程接收到了你的信息,不客气,我就是做耗时操作的")
console.log("2妙后我还会给你发送个信息")
setTimeout(() => {
postMessage('如果还有耗时的操作,你还可以在开启一个worker线程呦')
}, 2000)

}
//worker自己关闭
// self.close()
}
})()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<!DOCTYPE html>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
<html>
<body>
<div id="bt">
<p style="color:red">模拟元素1</p>
<p style="color:red">模拟元素2</p>
<p style="color:red">模拟元素3</p>
<p style="color:red">模拟元素4</p>
<div class="ld" style="color:green">数据加载中。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。</div>
<div class="main"></div>
<p style="color:red">模拟元素5</p>
<p style="color:red">模拟元素6</p>
<p style="color:red">模拟元素7</p>
</div>
<script>
// 引入的js文件必须和当前页面同源,不可跨域
let w = new Worker("./worker2.js");
$(".ld").show()
// console.log("Worker线程处理完耗时操作后,主线程接会收到处理数据")
w.onmessage = function(e) {
let listData = e.data
console.log(listData)
let strs = ''
for (let j = 0; j < listData.length; j++) {
const element = listData[j];
strs += '<p>' + '这是接收到的数据' + j + '</p>'
}
$(".main").append(strs)
$(".ld").hide()
}
w.postMessage('你发送的数据我收到了,辛苦了')
//主线程关闭子线程
// w.terminate()
</script>
</body>
</html>


SharedWorker一种特殊的worker,详细内容查看SharedWorker

sharedWorker 顾名思义,是 worker 的一种,可以由所有同源的页面共享。同Worker的api一样,传入js的url,就可以注册一个 sharedWorker 实例,
sharedWorker通过port来发送和接收消息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let myWorker = new SharedWorker('worker.js');

//主线程和共享线程都是通过 postMessage() 方法来发送消息
myWorker.port.postMessage('发送消息');

//接收消息都是使用onmessage = (e)=>{},或者 addEventListener('message', (e)=>{})
myWorker.port.onmessage = (e)=>{console.log(e.data)} ;

//关闭启动和worker是有点区别的
线程通过 worker.port.start() 启动
线程通过 worker.port.close() 关闭

SharedWorker使用注意要点同worKer一样

简单使用Demo

  • shareWk.js 共享线程,里面存储计数器counter
  • A.html 刷新页面,通知counter+1
  • B.html 刷新页面,查看counter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 计时器
let counter = 0
// 监听连接
self.addEventListener('connect', (e) => {
const port = e.ports[0]
port.onmessage = (res) => {
console.log('A,B页面共享的信息:', res.data)
switch (res.data) {
case 'add':
counter++
break
}
console.log('counter:', counter)
port.postMessage(counter)
}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>A页面</title>
</head>
<body>
<h1>A页面</h1>
<script>
// 兼容性判断
if (!SharedWorker) {
throw new Error('当前浏览器不支持SharedWorker')
}
// 创建共享线程
const worker = new SharedWorker('shareWk.js')
// 启动线程端口
worker.port.start()
// 向共享线程发送消息
worker.port.postMessage('add')
// 线程监听消息 2种方式都可以
// worker.port.addEventListener('message', (e) => {
// console.log('A页面共享线程counter值:', e.data)
// })
worker.port.onmessage = (e) => {
console.log('A页面取到counter值:', e.data)
}
console.log(worker.port)
</script>
</body>

</html>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>B页面</title>
</head>
<body>
<h1>B页面</h1>
<script>
// 兼容性判断
if (!SharedWorker) {
throw new Error('当前浏览器不支持SharedWorker')
}
// 创建共享线程
const worker = new SharedWorker('shareWk.js')
// 启动线程端口
worker.port.start()
// 向共享线程发送消息
worker.port.postMessage('get counter')
// 线程监听消息
// 线程监听消息 2种方式都可以
// worker.port.addEventListener('message', (e) => {
// console.log('B页面共享线程counter值:', e.data)
// })
worker.port.onmessage = (e) => {
console.log('B页面取到counter值:', e.data)
}
</script>
</body>
</html>

SharedWorker调试注意点

SharedWorker不能直接在页面调试面板查看,需要浏览器输入 chrome://inspect
然后点击inspect查看

浅谈跨域

一、什么是跨域

要知道什么是跨域,需要先知道另一个名词【同源政策】。
同源政策:是一个重要的安全政策,它用于限制一个origin的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。
而什么才是同源呢?同源就是两个页面具有相同的协议,域名和端口 均一致。所以根据上面的了解,源=协议+主机/域名+端口,当源不同的时候也就是跨域了
例如:

crossDomain

当前页面url 被请求页面url 是否跨域 原因
http://www.test.com/ http://www.test.com/index.html 同源
http://www.test.com/ https://www.test.com/index.html 协议不同(http/https)
http://www.test.com/ http://www.baidu.com/ 主域名不同(test/baidu)
http://www.test.com/ http://blog.test.com/ 子域名不同(www/blog)
http://www.test.com:8080 http://www.test.com:7001/ 端口号不同(8080/7001)

注意:

在默认情况下 http 可以省略端口 80, https 省略 443。

二、跨域的限制

  • Cookie,LocalStorage,IndexDB 等存储性内容无法读取
  • DOM 节点无法访问
  • Ajax 请求发出去了,但是响应被浏览器拦截了

基本我们说的跨域是在第三点请求上面。
ajax请求报错:

1
2
Access to XMLHttpRequest at 'xxx' from origin 'xxx' has been block by CORS,
policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

三、解决跨域

方法一:JSONP

JSONP 是 JSON with Padding 的缩写,主要就是利用了 script 标签没有跨域限制的这个特性来完成的。
思路:网页通过添加一个